#!/usr/bin/python
# author: zdzhjx@gmail.com
# version: 0.1
import os
import shutil
import random
from hashlib import md5
import threading
import traceback
class FileDic():
    def __init__( self, tmdir='.', name=None ):
        if not name:
            name = str(1000000*random.random())
        self.m_name = name
        self.m_dir = tmdir + '/' + self.m_name
        try:
            os.mkdir( self.m_dir )
        except:
            traceback.print_exc()

        self.m_dic = {}
        self.m_lock = threading.Lock()

    def __savedata( self, k,data ):
        # save the real data to file
        try:

            fname = self.__keymapfile( k )
            try:
                os.mkdir( os.path.dirname( fname ) )
            except:
                pass
            fd = open( fname, "w" ) 
            fd.write( data )
            fd.close()
        except:
            traceback.print_exc()
            return False
        return True

    def __readdata( self, k ):
        # get real data from file
        data = ""
        try:
            fname = self.__keymapfile( k )
            fd = open( fname, "r" )
            data = fd.read()
            fd.close()
        except:
            traceback.print_exc()
        return data
    
    def __keymapfile( self, k ):
        # get file full path
        f = md5(k).hexdigest()
        return self.m_dir + '/' + f[:2] + '/' + f[2:]

    def popitem( self ):
        # pop one element
        try:
            self.m_lock.acquire()
            k, key = self.m_dic.popitem()
            data = self.__readdata( k )
            # 
            fname =  self.__keymapfile(k)
            os.unlink( fname )
        except:
            traceback.print_exc()
            import pdb
            pdb.set_trace()
            k = None
            data = None
        finally:
            self.m_lock.release()

        return ( k, data )

    def push( self, k , v ):
        # push a element
        try:
            self.m_lock.acquire()
            if self.__savedata( k, v ):
                self.m_dic[ k ] = 1 
        except:
            traceback.print_exc()
            return False
        finally:
            self.m_lock.release()
        return True
    
    def getele( self, k ):
        # return data by k
        try:
            self.m_lock.acquire()
            data = self.__readdata( k )
        except:
            traceback.print_exc()
        finally:
            self.m_lock.release()        
        return data
    
    def update( self, k , v ):
        # update v by k
        return self.push( k, v )

    def has_key( self, k ):
        # k in dic or not
        if k in self.m_dic:
            return True
        else:
            return False
    
    def qsize( self ):
        # return size of the FileDic
        return len(self.m_dic)
    
    # for compitable of dic
    def __setitem__( self, k, v ):
        self.push( k, v )
        return 
    
    def __getitem__( self, k ):
        data = self.getele( k )
        return data
    
    def __delitem__( self, k ):
        if self.has_key( k ):
            try:
                self.m_lock.acquire()
                os.remove( self.__keymapfile(k) )
                del self.m_dic[ k ]
            except:
                traceback.print_exc()
            finally:
                self.m_lock.release()

    def __len__( self ):
        return len( self.m_dic )

    def keys( self ):
        return self.m_dic.keys()

    def __del__( self ):
        try:
            shutil.rmtree( self.m_dir )
        except:
            traceback.print_exc()

if __name__ == '__main__':
    # for test
    q = FileDic()
    print 'push a', q.push( 'a', '100' )
    print 'del a', q.getele('a')
    print 'popitem ',q.popitem()
    q['aa'] = '10000'
    print 'set aa'
    print 'aa', q['aa']
    del q['aa']
    print 'del aa'
    print 'dump keys', q.keys()
    q['ok'] = 'aaaaa'
    print q.keys()

